<?php
/**
 * 扫描controller目录，提取出控制器方法入库
 * Created by PhpStorm.
 * User: cy
 * Date: 19-1-1
 * Time: 下午8:05
 */

namespace app\models\forms;


use app\common\Helpers;
use app\components\exceptions\Exception;
use Yii;
use yii\helpers\ArrayHelper;
use yii\rbac\Permission;

class PermissionInit
{
    private $controllerDir;//控制器目录

    private $authManager;//yii权限控制类，用于数据入库

    private $controllerFileList;//控制器文件列表

    private $permissionList;//权限列表

    private $permissionDesc;//权限描述

    private $classNameSpace;//控制器的命名空间

    private $roleOnly;//权限设置，架在yii的rbac之上，控制权限只能由那些角色访问。
    private $roleOnlyFilePath;//配置文件路径

    /**
     * PermissionInit constructor.
     * @param $controllerDir
     * @throws Exception
     */
    public function __construct($controllerDir)
    {
        if (!is_dir($controllerDir)) throw new Exception('需要一个controller目录');

        $this->controllerDir = $controllerDir;

        $this->authManager = Yii::$app->authManager;

        $this->permissionList=[];

        $this->roleOnlyFilePath = __DIR__ . '/role_only.php';
    }

    public function run()
    {
        $this->setControllerFileList()
            ->setNameSpace()
            ->setPermissionList()
            ->save();
    }

    /**
     * 设置controllerList
     * @return $this
     */
    protected function setControllerFileList()
    {
        $controllerList = scandir($this->controllerDir);
        unset($controllerList[0]);
        unset($controllerList[1]);

        $this->controllerFileList = $controllerList;

        return $this;


    }


    /**
     * 设置权限列表
     */
    protected function setPermissionList()
    {
        foreach ($this->controllerFileList as $controller) {
            $className = $this->classNameSpace . '\\' . str_replace('.php', '', $controller) . '';
            // $reflection = new \ReflectionClass($className);

            $actionList = get_class_methods($className);
            $permissionDesc=ArrayHelper::getValue(get_class_vars($className),'permissionDesc');

            //配置成false 跳过这个控制器
            if($permissionDesc===false) continue;

            //转数组
            $permissionDesc=$permissionDesc?:[];


            //解析出controller的url格式
            $controllerUrl = self::parseHumpToUrl($controller, 'Controller.php');

            foreach ($actionList as $action) {
                if (strpos($action, 'action') === false || $action === 'actions') continue;

                //压入通配符,放在这里避免没有action动作的控制器产生权限
                //$this->setPermissionDesc($permissionDesc, $controllerUrl, '*');

                //解析action的url
                $actionUrl = self::parseHumpToUrl($action, 'action');

                //压入方法
                $this->setPermissionDesc($permissionDesc, $controllerUrl, $actionUrl);

            }

        }

        return $this;
    }

    /**
     * 设定控制器类的命名空间以供反射使用
     */
    protected function setNameSpace()
    {

        $controllerFilePath = $this->controllerDir . '/' . $this->controllerFileList[2];

        $content = file_get_contents($controllerFilePath);

        //提取命名空间
        preg_match('/namespace ([a-zA-Z_\\\]+)\;/', $content, $namespaceArr);

        if (empty($namespaceArr[1])) throw new Exception('控制器命名空间获取失败');

        $this->classNameSpace = $namespaceArr[1];

        return $this;
    }

    /**
     * 将权限存入数据库
     * @throws
     */
    protected function save()
    {
        if (empty($this->permissionList) || !is_array($this->permissionList)) {
            throw new Exception('从文件获取权限列表失败');
        }

        $trans = Yii::$app->db->beginTransaction();
        try {
            foreach ($this->permissionList as $permission) {
                if ($obj=$this->authManager->getPermission($permission)){
                    //更新
                    $this->setPermissionObj($obj);
                    $this->authManager->update($obj->name, $obj);

                }else{
                    //添加
                    $obj = $this->authManager->createPermission($permission);
                    $this->setPermissionObj($obj);

                    $this->authManager->add($obj);
                }

            }

            $this->createRoleOnlyFile();

            $trans->rollBack();

        } catch (\Exception $e) {
            $trans->rollBack();
            throw $e;
        }


    }

    /**
     * 将驼峰命名转换为yii的url
     * @param $name
     * @param string $replace
     * @return string
     */
    public static function parseHumpToUrl($name, $replace = 'action')
    {
        return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . '-' . "$2",
            str_replace($replace, '', $name)));
    }


    /**
     * 设置权限对象
     * @param Permission $obj
     * @param $data
     * @throws
     * @return Permission
     */
    protected function setPermissionObj(Permission $obj)
    {
        $data = ArrayHelper::getValue($this->permissionDesc, $obj->name,$obj->name);

        //兼容array，string
        $data=is_string($data)?['description'=>$data,'role_only'=>['*']]:$data;

        $obj->description = ArrayHelper::getValue($data, 'description', $obj->name);

        //设置特定角色和权限数据
        $roleDeny=ArrayHelper::getValue($data, 'role_only', ['*']);
        $this->roleOnly[$obj->name]=$roleDeny;



        return $obj;
    }

    /**
     * 设置权限的额外信息
     * @param $permissionDesc
     * @param $controllerUrl
     * @param $actionUrl
     */
    protected function setPermissionDesc($permissionDesc,$controllerUrl,$actionUrl)
    {
        $controllerUrlAll=$controllerUrl . '/'.$actionUrl;

        if(!in_array($controllerUrlAll, $this->permissionList)){
            $this->permissionList[]=$controllerUrlAll;
            $this->permissionDesc[$controllerUrlAll]=ArrayHelper::getValue($permissionDesc,$actionUrl,$controllerUrlAll);
        }


    }


    /**
     * 创建特定角色和权限限制文件
     */
    protected function createRoleOnlyFile()
    {
        $content='<?php return '.PHP_EOL;

        $content .= var_export($this->roleOnly, true).';';

        file_put_contents($this->roleOnlyFilePath, $content);

    }


}